<!-- 代码查看器组件 -->
<template>
	<div class="code-viewer-overlay" :class="{ visible: open, fullscreen: isFullscreen }" @click="handleOverlayClick">
		<div class="code-viewer-panel" :class="{ visible: open, 'dark-theme': isDarkMode, fullscreen: isFullscreen }" @click.stop>
			<!-- 头部工具栏 -->
			<div class="viewer-header">
				<div class="header-left">
					<div class="file-name" :title="fileName">{{ fileName }}</div>
				</div>
				<div class="header-right">
					<!-- 搜索框 -->
					<div class="search-container" v-if="showSearch">
						<div class="search-input">
							<a-input
								v-model:value="searchText"
								placeholder="搜索代码..."
								@keyup.enter="findNext"
								@input="handleSearch"
								size="small"
								ref="searchInputRef"
							/>
						</div>
						<div class="search-info" v-if="searchResults.length > 0">
							<span class="search-position"> {{ currentSearchIndex + 1 }} / {{ searchResults.length }} </span>
						</div>
						<div class="search-controls">
							<a-button size="small" @click="findPrevious" :disabled="searchResults.length === 0">
								<UpOutlined />
							</a-button>
							<a-button size="small" @click="findNext" :disabled="searchResults.length === 0">
								<DownOutlined />
							</a-button>
							<a-button size="small" @click="closeSearch">
								<CloseOutlined />
							</a-button>
						</div>
					</div>

					<!-- 工具按钮 -->
					<a-tooltip title="搜索代码" placement="bottom" :get-popup-container="getPopupContainer">
						<a-button type="text" size="small" @click="toggleSearch" :class="{ active: showSearch }">
							<SearchOutlined />
						</a-button>
					</a-tooltip>

					<a-tooltip title="展开全部" placement="bottom" :get-popup-container="getPopupContainer">
						<a-button type="text" size="small" @click="expandAll">
							<UpOutlined />
						</a-button>
					</a-tooltip>

					<a-tooltip title="折叠全部" placement="bottom" :get-popup-container="getPopupContainer">
						<a-button type="text" size="small" @click="foldAll">
							<DownOutlined />
						</a-button>
					</a-tooltip>

					<a-tooltip :title="isFullscreen ? '退出全屏' : '全屏显示'" placement="bottom" :get-popup-container="getPopupContainer">
						<a-button type="text" size="small" @click="toggleFullscreen">
							<FullscreenExitOutlined v-if="isFullscreen" />
							<FullscreenOutlined v-else />
						</a-button>
					</a-tooltip>

					<a-tooltip title="复制代码" placement="bottom" :get-popup-container="getPopupContainer">
						<a-button type="text" size="small" @click="copyCode" :loading="copying">
							<CopyOutlined v-if="!copying" />
							<CheckOutlined v-else style="color: #52c41a" />
						</a-button>
					</a-tooltip>

					<a-tooltip :title="isDarkMode ? '切换到明亮模式' : '切换到暗黑模式'" placement="bottom" :get-popup-container="getPopupContainer">
						<a-button type="text" size="small" @click="toggleTheme">
							<BulbFilled v-if="isDarkMode" style="color: #fadb14" />
							<BulbOutlined v-else />
						</a-button>
					</a-tooltip>

					<a-tooltip title="关闭" placement="bottom" :get-popup-container="getPopupContainer">
						<a-button type="text" size="small" @click="closeViewer">
							<CloseOutlined />
						</a-button>
					</a-tooltip>
				</div>
			</div>

			<!-- 代码内容区域 -->
			<div class="viewer-content" ref="contentRef">
				<div class="code-container" ref="codeContainerRef">
					<pre
						class="code-block"
						:class="{ 'dark-theme': isDarkMode }"
						@click="handleCodeClick"
					><code ref="codeRef" :class="`language-${language || 'text'}`" v-html="highlightedCode"></code></pre>
				</div>
			</div>
		</div>
	</div>
</template>

<script setup lang="ts">
import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
import { message } from 'ant-design-vue'
import {
	SearchOutlined,
	CopyOutlined,
	CheckOutlined,
	BulbOutlined,
	BulbFilled,
	CloseOutlined,
	UpOutlined,
	DownOutlined,
	FullscreenOutlined,
	FullscreenExitOutlined,
} from '@ant-design/icons-vue'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'
import 'highlight.js/styles/github-dark.css'

interface Props {
	open: boolean
	fileName: string
	codeContent: string
	language?: string
}

interface Emits {
	(e: 'update:open', value: boolean): void
	(e: 'close'): void
}

const props = defineProps<Props>()
const emit = defineEmits<Emits>()

// 组件引用
const contentRef = ref<HTMLElement>()
const codeRef = ref<HTMLElement>()
const codeContainerRef = ref<HTMLElement>()
const searchInputRef = ref<any>()

// 主题相关
const isDarkMode = ref(true)

// 全屏相关
const isFullscreen = ref(false)

// 搜索相关
const showSearch = ref(false)
const searchText = ref('')
const searchResults = ref<{ start: number; end: number; element?: HTMLElement }[]>([])
const currentSearchIndex = ref(-1)

// 代码折叠相关状态
const foldableBlocks = ref<{ start: number; end: number; type: string; level: number }[]>([]) // 可折叠的代码块
const foldedBlocks = ref<Set<number>>(new Set()) // 已折叠的代码块（存储起始行号）

// 复制相关
const copying = ref(false)

// 代码高亮处理
const highlightedCode = computed(() => {
	if (!props.codeContent) return ''

	try {
		const language = normalizeLanguage(props.language || 'text')

		// 先检测可折叠块（在高亮处理之前）
		foldableBlocks.value = detectFoldableBlocks(props.codeContent)

		// 检查highlight.js是否支持该语言
		if (language === 'text' || !hljs.getLanguage(language)) {
			// 使用自定义高亮逻辑
			return addLineNumbers(customHighlight(props.codeContent, language))
		}

		// 使用highlight.js
		const highlighted = hljs.highlight(props.codeContent, { language }).value
		return addLineNumbers(highlighted)
	} catch (error) {
		console.error('代码高亮失败:', error)
		// 降级到自定义高亮
		return addLineNumbers(customHighlight(props.codeContent, props.language || 'text'))
	}
})

// 语言标准化映射
const normalizeLanguage = (language: string): string => {
	const languageMap: Record<string, string> = {
		js: 'javascript',
		ts: 'typescript',
		jsx: 'javascript',
		tsx: 'typescript',
		py: 'python',
		sh: 'bash',
		shell: 'bash',
		yml: 'yaml',
		md: 'markdown',
		vue: 'xml', // Vue文件使用XML高亮作为基础
		txt: 'text',
	}

	return languageMap[language.toLowerCase()] || language.toLowerCase()
}

// 自定义代码高亮（用于highlight.js不支持的语言）
const customHighlight = (code: string, language: string): string => {
	let highlighted = code

	// Vue模板特殊处理
	if (language === 'vue') {
		// Vue指令高亮
		highlighted = highlighted.replace(/v-[a-zA-Z-]+/g, '<span class="hljs-attr">$&</span>')
		// Vue插值表达式
		highlighted = highlighted.replace(/{{[^}]*}}/g, '<span class="hljs-template-variable">$&</span>')
		// HTML标签
		highlighted = highlighted.replace(/<\/?([a-zA-Z][a-zA-Z0-9-]*)[^>]*>/g, (match, tagName) => {
			return match.replace(new RegExp(`\\b${tagName}\\b`, 'g'), `<span class="hljs-name">${tagName}</span>`)
		})
		// HTML属性
		highlighted = highlighted.replace(/\s([a-zA-Z-]+)=/g, ' <span class="hljs-attr">$1</span>=')
	}

	// JavaScript/TypeScript关键字
	if (['javascript', 'typescript', 'js', 'ts'].includes(language)) {
		const keywords = [
			'function',
			'const',
			'let',
			'var',
			'if',
			'else',
			'for',
			'while',
			'return',
			'class',
			'interface',
			'type',
			'import',
			'export',
			'from',
			'as',
			'true',
			'false',
			'null',
			'undefined',
			'async',
			'await',
		]
		keywords.forEach((keyword) => {
			const regex = new RegExp(`\\b${keyword}\\b`, 'g')
			highlighted = highlighted.replace(regex, `<span class="hljs-keyword">${keyword}</span>`)
		})
	}

	// 通用高亮规则
	// 字符串
	highlighted = highlighted.replace(/(['"`])((?:\\.|(?!\1)[^\\])*)\1/g, '<span class="hljs-string">$&</span>')
	// 注释
	highlighted = highlighted.replace(/\/\/.*$/gm, '<span class="hljs-comment">$&</span>')
	highlighted = highlighted.replace(/\/\*[\s\S]*?\*\//g, '<span class="hljs-comment">$&</span>')
	// 数字
	highlighted = highlighted.replace(/\b\d+(\.\d+)?\b/g, '<span class="hljs-number">$&</span>')

	// 最后进行HTML转义，但保留我们添加的高亮标签
	highlighted = escapeHtmlButKeepHighlight(highlighted)

	return highlighted
}

// 转义HTML字符
const escapeHtml = (text: string): string => {
	return text
		.replace(/&/g, '&amp;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;')
		.replace(/"/g, '&quot;')
		.replace(/'/g, '&#39;')
}

// 转义HTML字符但保留高亮标签
const escapeHtmlButKeepHighlight = (text: string): string => {
	// 先保护高亮标签
	const highlightTags: string[] = []
	let tagIndex = 0

	// 提取所有高亮标签
	let processedText = text.replace(/<span class="hljs-[^"]*"[^>]*>|<\/span>/g, (match) => {
		const placeholder = `__HIGHLIGHT_TAG_${tagIndex}__`
		highlightTags[tagIndex] = match
		tagIndex++
		return placeholder
	})

	// 转义HTML字符
	processedText = processedText
		.replace(/&/g, '&amp;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;')
		.replace(/"/g, '&quot;')
		.replace(/'/g, '&#39;')

	// 恢复高亮标签
	for (let i = 0; i < highlightTags.length; i++) {
		processedText = processedText.replace(`__HIGHLIGHT_TAG_${i}__`, highlightTags[i])
	}

	return processedText
}

// 识别可折叠的代码块
const detectFoldableBlocks = (code: string): { start: number; end: number; type: string; level: number }[] => {
	const lines = code.split('\n')
	const blocks: { start: number; end: number; type: string; level: number }[] = []
	const stack: { line: number; type: string; level: number; tagName?: string }[] = []

	for (let i = 0; i < lines.length; i++) {
		const line = lines[i].trim()
		const indent = lines[i].length - lines[i].trimStart().length

		// 检测HTML标签开始（支持单行和跨行的HTML元素）
		// 使用更宽松的正则表达式来匹配HTML标签开始
		const htmlOpenTagMatch = line.match(/<([a-zA-Z][a-zA-Z0-9-]*)/)
		if (htmlOpenTagMatch) {
			const tagName = htmlOpenTagMatch[1]
			// 排除自闭合标签
			if (
				![
					'img',
					'br',
					'hr',
					'input',
					'meta',
					'link',
					'area',
					'base',
					'col',
					'embed',
					'source',
					'track',
					'wbr',
				].includes(tagName.toLowerCase())
			) {
				// 检查是否是自闭合标签
				const isSelfClosing = line.includes('/>')

				if (!isSelfClosing) {
					// 检查是否是同行闭合的标签
					const hasClosingTag = line.includes(`</${tagName}>`)

					if (hasClosingTag) {
						// 单行闭合标签，直接创建折叠块
						blocks.push({
							start: i,
							end: i,
							type: 'html',
							level: Math.floor(indent / 2),
						})
					} else {
						// 跨行标签，加入栈中等待匹配
						stack.push({ line: i, type: 'html', level: Math.floor(indent / 2), tagName })
					}
				}
			}
		}

		// 检测HTML标签结束
		const htmlCloseTagMatch = line.match(/<\/([a-zA-Z][a-zA-Z0-9-]*)>/)
		if (htmlCloseTagMatch && stack.length > 0) {
			const tagName = htmlCloseTagMatch[1]
			// 检查是否是单行闭合标签（开始和结束在同一行）
			const hasOpeningTag = line.includes(`<${tagName}`)

			// 如果是单行闭合标签，跳过处理，避免干扰栈匹配
			if (hasOpeningTag) {
				// 单行闭合标签已经在开始标签检测时处理过了，这里跳过
				continue
			}

			// 从栈顶开始查找最近的匹配标签（LIFO栈匹配）
			for (let j = stack.length - 1; j >= 0; j--) {
				const openBlock = stack[j]
				if (openBlock && openBlock.type === 'html' && openBlock.tagName === tagName) {
					// 只移除匹配的开始标签，保持栈的正确状态
					stack.splice(j, 1)
					blocks.push({
						start: openBlock.line,
						end: i,
						type: 'html',
						level: openBlock.level,
					})
					break
				}
			}
		}

		// 检测各种可折叠结构的开始
		if (line.includes('{')) {
			let blockType = 'block'

			// 识别不同类型的代码块
			if (/^(function|const\s+\w+\s*=\s*\(|class\s+|interface\s+|type\s+)/.test(line)) {
				blockType = 'function'
			} else if (/^(if\s*\(|for\s*\(|while\s*\(|switch\s*\()/.test(line)) {
				blockType = 'control'
			} else if (/^(try\s*{|catch\s*\(|finally\s*{)/.test(line)) {
				blockType = 'exception'
			} else if (line.includes(':{') || line.includes(': {')) {
				blockType = 'object'
			} else if (line.includes('[')) {
				blockType = 'array'
			}

			stack.push({ line: i, type: blockType, level: Math.floor(indent / 2) })
		}

		// 检测代码块结束
		if (line.includes('}') && stack.length > 0) {
			const openBlock = stack[stack.length - 1]
			if (openBlock && openBlock.type !== 'html' && i > openBlock.line) {
				stack.pop()
				blocks.push({
					start: openBlock.line,
					end: i,
					type: openBlock.type,
					level: openBlock.level,
				})
			}
		}
	}

	return blocks.filter((block) => block.end - block.start > 0) // 过滤掉单行块
}

// 添加行号和折叠控件
const addLineNumbers = (code: string): string => {
	const lines = code.split('\n')

	// 可折叠块已在highlightedCode中检测

	return lines
		.map((line, index) => {
			const lineNumber = index + 1
			const isFoldStart = foldableBlocks.value.some((block) => block.start === index)
			const isFolded = foldedBlocks.value.has(index)
			const foldBlock = foldableBlocks.value.find((block) => block.start === index)

			// 如果当前行在已折叠的块内且不是起始行，则隐藏
			const isHidden = foldableBlocks.value.some(
				(block) => foldedBlocks.value.has(block.start) && index > block.start && index <= block.end
			)

			let foldControl = ''
			if (isFoldStart) {
				const foldIcon = isFolded ? '▶' : '▼'
				foldControl = `<span class="fold-control" data-line="${index}" data-type="${foldBlock?.type}">${foldIcon}</span>`
			}

			const hiddenClass = isHidden ? ' hidden-line' : ''
			const foldedClass = isFolded ? ' folded-start' : ''

			return `<div class="code-line${hiddenClass}${foldedClass}"><span class="line-number"><span class="line-num">${lineNumber}</span>${isFoldStart ? `<span class="fold-control-wrapper">${foldControl}</span>` : '<span class="fold-control-wrapper"></span>'}</span><span class="line-content">${line || ' '}</span></div>`
		})
		.join('')
}

// 切换主题
const toggleTheme = () => {
	isDarkMode.value = !isDarkMode.value
}

// 切换全屏模式
const toggleFullscreen = () => {
	isFullscreen.value = !isFullscreen.value
}

// 复制代码
const copyCode = async () => {
	try {
		copying.value = true
		await navigator.clipboard.writeText(props.codeContent)
		message.success('代码已复制到剪贴板')

		// 显示复制成功图标
		setTimeout(() => {
			copying.value = false
		}, 1500)
	} catch (error) {
		message.error('复制失败，请手动复制')
		copying.value = false
	}
}

// 获取当前选中的文本
const getSelectedText = (): string => {
	const selection = window.getSelection()
	if (selection && selection.rangeCount > 0 && !selection.isCollapsed) {
		const selectedText = selection.toString().trim()
		// 只有当选中文本不为空且长度合理时才返回
		if (selectedText && selectedText.length > 0 && selectedText.length <= 100) {
			// 清除选中状态，避免影响后续操作
			selection.removeAllRanges()
			return selectedText
		}
	}
	return ''
}

// 切换搜索
const toggleSearch = () => {
	const selectedText = getSelectedText()

	if (showSearch.value) {
		// 如果搜索框已经显示，则聚焦到搜索框
		if (selectedText) {
			// 如果有选中文本，则填充到搜索框
			searchText.value = selectedText
		}
		nextTick(() => {
			searchInputRef.value?.focus()
			if (selectedText) {
				// 如果填充了新文本，则立即搜索
				performSearch()
			}
		})
	} else {
		// 显示搜索框并聚焦
		showSearch.value = true
		if (selectedText) {
			// 如果有选中文本，则填充到搜索框
			searchText.value = selectedText
		}
		nextTick(() => {
			searchInputRef.value?.focus()
			if (selectedText) {
				// 如果填充了新文本，则立即搜索
				performSearch()
			}
		})
	}
}

// 切换代码块折叠状态
const toggleFold = (lineIndex: number) => {
	const block = foldableBlocks.value.find((b) => b.start === lineIndex)
	if (!block) return

	if (foldedBlocks.value.has(lineIndex)) {
		foldedBlocks.value.delete(lineIndex)
	} else {
		foldedBlocks.value.add(lineIndex)
	}

	// 重新渲染代码
	updateCodeDisplay()
}

// 展开所有折叠的代码块
const expandAll = () => {
	foldedBlocks.value.clear()
	updateCodeDisplay()
}

// 折叠所有可折叠的代码块
const foldAll = () => {
	foldableBlocks.value.forEach((block) => {
		foldedBlocks.value.add(block.start)
	})
	updateCodeDisplay()
}

// 更新代码显示
const updateCodeDisplay = () => {
	nextTick(() => {
		// 触发重新计算highlightedCode
		if (searchText.value.trim()) {
			performSearch()
		}
	})
}

// 关闭搜索
const closeSearch = () => {
	showSearch.value = false
	searchText.value = ''
	clearSearchHighlights()
}

// 处理搜索
const handleSearch = () => {
	if (!searchText.value.trim()) {
		clearSearchHighlights()
		return
	}

	performSearch()
}

// 执行搜索
const performSearch = () => {
	clearSearchHighlights()

	if (!searchText.value.trim() || !codeRef.value) return

	const searchTerm = searchText.value
	const codeElement = codeRef.value

	// 获取原始代码内容进行搜索（支持特殊字符）
	const originalCode = props.codeContent
	const results: { start: number; end: number }[] = []

	// 查找所有匹配项（区分大小写，支持特殊字符）
	let index = 0
	while (index < originalCode.length) {
		const foundIndex = originalCode.indexOf(searchTerm, index)
		if (foundIndex === -1) break

		results.push({
			start: foundIndex,
			end: foundIndex + searchTerm.length,
		})

		index = foundIndex + 1
	}

	searchResults.value = results
	currentSearchIndex.value = results.length > 0 ? 0 : -1

	// 高亮显示搜索结果
	highlightSearchResults()

	// 滚动到第一个结果
	if (results.length > 0) {
		scrollToSearchResult(0)
	}
}

// 获取不包含行号的代码文本
const getCodeTextWithoutLineNumbers = (codeElement: HTMLElement): string => {
	// 获取所有行内容元素的文本
	const lineContents = codeElement.querySelectorAll('.line-content')
	const lines: string[] = []

	lineContents.forEach((lineContent) => {
		// 获取纯文本内容，去除HTML标签
		const text = lineContent.textContent || lineContent.innerText || ''
		lines.push(text)
	})

	return lines.join('\n')
}

// 创建浮动提示元素
const createFloatingTip = (text: string, element: HTMLElement) => {
	// 移除已存在的提示
	removeFloatingTip()

	const tip = document.createElement('div')
	tip.className = 'search-floating-tip'
	tip.textContent = text
	tip.style.cssText = `
		position: absolute;
		background: ${isDarkMode.value ? '#ff6b35' : '#ff4444'};
		color: white;
		padding: 4px 8px;
		border-radius: 4px;
		font-size: 12px;
		white-space: nowrap;
		z-index: 9999;
		pointer-events: none;
		box-shadow: 0 2px 8px rgba(0,0,0,0.3);
		transition: opacity 0.2s ease;
	`

	// 定位提示框
	const rect = element.getBoundingClientRect()
	const containerRect = codeContainerRef.value?.getBoundingClientRect()
	if (containerRect) {
		tip.style.left = `${rect.left - containerRect.left + rect.width + 10}px`
		tip.style.top = `${rect.top - containerRect.top}px`
	}

	codeContainerRef.value?.appendChild(tip)

	// 3秒后自动消失
	setTimeout(() => {
		removeFloatingTip()
	}, 3000)
}

// 移除浮动提示
const removeFloatingTip = () => {
	const existingTip = codeContainerRef.value?.querySelector('.search-floating-tip')
	if (existingTip) {
		existingTip.remove()
	}
}

// 高亮搜索结果
const highlightSearchResults = () => {
	if (!codeRef.value || searchResults.value.length === 0) return

	const codeElement = codeRef.value
	const searchTerm = searchText.value
	const originalCode = props.codeContent

	// 基于原始代码内容和搜索结果进行高亮
	const lines = originalCode.split('\n')
	let currentOffset = 0
	let matchIndex = 0

	// 遍历每一行，查找匹配项并高亮
	lines.forEach((line, lineIndex) => {
		const lineStart = currentOffset
		const lineEnd = currentOffset + line.length

		// 查找在当前行范围内的搜索结果
		const lineMatches = searchResults.value.filter((result) => result.start >= lineStart && result.start < lineEnd)

		if (lineMatches.length > 0) {
			// 获取对应的DOM行元素
			const lineElement = codeElement.querySelector(`.code-line:nth-child(${lineIndex + 1}) .line-content`)
			if (lineElement) {
				// 高亮当前行中的匹配项
				highlightInLine(lineElement as HTMLElement, lineMatches, lineStart, matchIndex)
				matchIndex += lineMatches.length
			}
		}

		// 更新偏移量（+1是为了换行符）
		currentOffset = lineEnd + 1
	})
}

// 在指定行中高亮匹配项
const highlightInLine = (
	lineElement: HTMLElement,
	matches: { start: number; end: number }[],
	lineStart: number,
	startMatchIndex: number
) => {
	// 获取行的原始文本内容
	const originalLineText = lineElement.textContent || ''

	// 按照匹配位置排序
	const sortedMatches = matches
		.map((match, index) => ({
			...match,
			relativeStart: match.start - lineStart,
			relativeEnd: match.end - lineStart,
			matchIndex: startMatchIndex + index,
		}))
		.sort((a, b) => a.relativeStart - b.relativeStart)

	// 构建新的HTML内容
	let newHTML = ''
	let lastEnd = 0

	sortedMatches.forEach((match) => {
		// 添加匹配前的文本
		if (match.relativeStart > lastEnd) {
			newHTML += escapeHtml(originalLineText.substring(lastEnd, match.relativeStart))
		}

		// 添加高亮的匹配文本
		const matchText = originalLineText.substring(match.relativeStart, match.relativeEnd)
		const isCurrentMatch = match.matchIndex === currentSearchIndex.value
		newHTML += `<span class="search-highlight ${isCurrentMatch ? 'current' : ''}" data-match-index="${match.matchIndex}">${escapeHtml(matchText)}</span>`

		lastEnd = match.relativeEnd
	})

	// 添加剩余的文本
	if (lastEnd < originalLineText.length) {
		newHTML += escapeHtml(originalLineText.substring(lastEnd))
	}

	// 更新DOM
	lineElement.innerHTML = newHTML
}

// 清除搜索高亮
const clearSearchHighlights = () => {
	if (!codeRef.value) return

	const highlights = codeRef.value.querySelectorAll('.search-highlight')
	highlights.forEach((highlight) => {
		const parent = highlight.parentNode
		if (parent) {
			parent.replaceChild(document.createTextNode(highlight.textContent || ''), highlight)
			parent.normalize() // 合并相邻的文本节点
		}
	})

	searchResults.value = []
	currentSearchIndex.value = -1
}

// 查找下一个
const findNext = () => {
	if (searchResults.value.length === 0) return

	currentSearchIndex.value = (currentSearchIndex.value + 1) % searchResults.value.length
	updateCurrentHighlight()
	scrollToSearchResult(currentSearchIndex.value)

	// 显示浮动提示
	setTimeout(() => {
		const currentHighlight = codeRef.value?.querySelector('.search-highlight.current')
		if (currentHighlight) {
			const matchText = currentHighlight.textContent || ''
			const lineElement = currentHighlight.closest('.code-line')
			if (lineElement) {
				const lineNumber = lineElement.querySelector('.line-number')?.textContent || ''
				const tipText = `第 ${lineNumber} 行: "${matchText}"`
				createFloatingTip(tipText, currentHighlight as HTMLElement)
			}
		}
	}, 300)
}

// 查找上一个
const findPrevious = () => {
	if (searchResults.value.length === 0) return

	currentSearchIndex.value =
		currentSearchIndex.value <= 0 ? searchResults.value.length - 1 : currentSearchIndex.value - 1
	updateCurrentHighlight()
	scrollToSearchResult(currentSearchIndex.value)

	// 显示浮动提示
	setTimeout(() => {
		const currentHighlight = codeRef.value?.querySelector('.search-highlight.current')
		if (currentHighlight) {
			const matchText = currentHighlight.textContent || ''
			const lineElement = currentHighlight.closest('.code-line')
			if (lineElement) {
				const lineNumber = lineElement.querySelector('.line-number')?.textContent || ''
				const tipText = `第 ${lineNumber} 行: "${matchText}"`
				createFloatingTip(tipText, currentHighlight as HTMLElement)
			}
		}
	}, 300)
}

// 更新当前高亮
const updateCurrentHighlight = () => {
	if (!codeRef.value) return

	const highlights = codeRef.value.querySelectorAll('.search-highlight')
	highlights.forEach((highlight, index) => {
		highlight.classList.toggle('current', index === currentSearchIndex.value)
	})
}

// 滚动到搜索结果
const scrollToSearchResult = (index: number) => {
	if (!codeRef.value || !contentRef.value) return

	// 等待DOM更新后再滚动
	nextTick(() => {
		// 查找当前高亮的搜索结果元素
		const highlights = codeRef.value?.querySelectorAll('.search-highlight')
		if (!highlights || index >= highlights.length) return

		const currentHighlight = highlights[index] as HTMLElement
		if (!currentHighlight) return

		// 使用原生的scrollIntoView实现居中滚动
		currentHighlight.scrollIntoView({
			behavior: 'smooth',
			block: 'center',
			inline: 'nearest',
		})
	})
}

// 关闭查看器
const closeViewer = () => {
	emit('update:open', false)
	emit('close')
}

// 获取弹出容器
const getPopupContainer = () => {
	return document.body
}

// 处理遮罩点击
const handleOverlayClick = () => {
	closeViewer()
}

// 处理代码点击事件（用于折叠控件）
const handleCodeClick = (event: MouseEvent) => {
	const target = event.target as HTMLElement
	if (target.classList.contains('fold-control')) {
		const lineIndex = parseInt(target.getAttribute('data-line') || '0')
		toggleFold(lineIndex)
		return
	}

	// 检测是否点击了省略号区域
	const codeLine = target.closest('.code-line')
	if (codeLine && codeLine.classList.contains('folded-start')) {
		// 获取点击位置相对于代码行的位置
		const rect = codeLine.getBoundingClientRect()
		const clickX = event.clientX - rect.left
		const lineContent = codeLine.querySelector('.line-content')
		if (lineContent) {
			const contentRect = lineContent.getBoundingClientRect()
			const contentEnd = contentRect.right - rect.left
			// 如果点击位置在内容结束后的省略号区域
			if (clickX > contentEnd) {
				const lineNumber = codeLine.querySelector('.line-number')?.textContent?.match(/\d+/)?.[0]
				if (lineNumber) {
					const lineIndex = parseInt(lineNumber) - 1
					toggleFold(lineIndex)
				}
			}
		}
	}
}

// 监听visible变化
watch(
	() => props.visible,
	(newVisible) => {
		if (!newVisible) {
			// 关闭时清理搜索状态
			closeSearch()
		}
	}
)

// 监听代码内容变化，重新进行搜索
watch(
	() => props.codeContent,
	() => {
		if (searchText.value.trim()) {
			nextTick(() => {
				performSearch()
			})
		}
	}
)

// 键盘事件处理
const handleKeydown = (event: KeyboardEvent) => {
	if (!props.visible) return

	if (event.key === 'Escape') {
		if (showSearch.value) {
			closeSearch()
		} else {
			closeViewer()
		}
	} else if (event.ctrlKey || event.metaKey) {
		if (event.key === 'f') {
			event.preventDefault()
			toggleSearch()
		} else if (event.key === 'c') {
			// 只有在搜索框没有焦点且没有文本选中时才触发复制代码功能
			const activeElement = document.activeElement
			const hasSelection = window.getSelection()?.toString().length > 0
			const isSearchInputFocused = activeElement === searchInputRef.value?.$el?.querySelector('input')

			if (!hasSelection && !isSearchInputFocused && !showSearch.value) {
				event.preventDefault()
				copyCode()
			}
			// 如果有文本选中或搜索框有焦点，让浏览器处理默认的复制行为
		}
	}
}

// 生命周期
onMounted(() => {
	document.addEventListener('keydown', handleKeydown)
})

onUnmounted(() => {
	document.removeEventListener('keydown', handleKeydown)
})
</script>

<style scoped lang="scss">
.code-viewer-overlay {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(0, 0, 0, 0.5);
	z-index: 9999;
	opacity: 0;
	visibility: hidden;
	transition: all 0.3s ease;

	&.visible {
		opacity: 1;
		visibility: visible;
	}
}

.code-viewer-panel {
	position: absolute;
	top: 0;
	right: 0;
	width: 50%;
	height: 100%;
	background: #ffffff;
	border-left: 1px solid #e8e8e8;
	display: flex;
	flex-direction: column;
	transform: translateX(100%);
	transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
	box-shadow: -4px 0 24px rgba(0, 0, 0, 0.12);

	&.visible {
		transform: translateX(0);
	}

	&.fullscreen {
		width: 100%;
		left: 0;
		right: 0;
		border-left: none;
		box-shadow: none;
	}

	&.dark-theme {
		background: #1e1e1e;
		border-left-color: #333;
		color: #d4d4d4;
		backdrop-filter: blur(20px);

		.viewer-header {
			background: linear-gradient(135deg, #2d2d30 0%, #1e1e1e 100%);
			border-bottom-color: #3e3e42;

			.file-name {
				color: #d4d4d4;
			}
		}

		.viewer-content {
			background: #0f1419;
			color: #e6ffed;
		}

		.search-input {
			:deep(.ant-input) {
				background: #3c3c3c;
				border-color: #555;
				color: #d4d4d4;

				&:focus {
					border-color: #1890ff;
					box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
				}
			}

			:deep(.ant-input::placeholder) {
				color: #888;
			}
		}

		.search-count {
			color: #d4d4d4;
		}

		// 暗黑主题下的按钮样式
		:deep(.ant-btn) {
			color: #e6edf3 !important;
			border-color: #30363d !important;
			background: transparent !important;

			&:hover {
				color: #ffffff !important;
				background: #21262d !important;
				border-color: #8b949e !important;
			}

			&:focus {
				color: #ffffff !important;
				background: #21262d !important;
				border-color: #58a6ff !important;
				box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.3) !important;
			}

			&:active {
				color: #ffffff !important;
				background: #30363d !important;
			}

			&.active {
				background: #1890ff !important;
				color: white !important;

				&:hover {
					background: #40a9ff !important;
				}
			}
		}

		// 暗黑主题下的代码高亮样式 - 参考豆包风格
		.code-block {
			&.dark-theme code {
				color: #e6edf3;
				background: #0d1117;

				// 关键字 - 亮红色
				:deep(.hljs-keyword),
				:deep(.hljs-selector-tag),
				:deep(.hljs-literal),
				:deep(.hljs-section),
				:deep(.hljs-link) {
					color: #ff7b72;
					font-weight: 600;
				}

				// 字符串 - 亮绿色
				:deep(.hljs-string) {
					color: #7ee787;
					font-weight: 500;
				}

				// 注释 - 灰色
				:deep(.hljs-comment),
				:deep(.hljs-quote) {
					color: #8b949e;
					font-style: italic;
					font-weight: 400;
				}

				// 数字 - 亮蓝色
				:deep(.hljs-number) {
					color: #79c0ff;
					font-weight: 500;
				}

				// 内置函数 - 亮蓝色
				:deep(.hljs-built_in),
				:deep(.hljs-builtin-name) {
					color: #79c0ff;
					font-weight: 500;
				}

				// 函数名 - 亮紫色
				:deep(.hljs-title.function_),
				:deep(.hljs-function),
				:deep(.hljs-title) {
					color: #d2a8ff;
					font-weight: 600;
				}

				// 属性和标签属性 - 亮紫色
				:deep(.hljs-attr),
				:deep(.hljs-attribute) {
					color: #d2a8ff;
					font-weight: 500;
				}

				// HTML标签名 - 亮绿色
				:deep(.hljs-name),
				:deep(.hljs-tag) {
					color: #7ee787;
					font-weight: 600;
				}

				// 变量和模板变量 - 亮橙色
				:deep(.hljs-variable),
				:deep(.hljs-template-variable) {
					color: #ffa657;
					font-weight: 500;
					background: rgba(255, 166, 87, 0.15);
					padding: 2px 4px;
					border-radius: 3px;
				}

				// 类型 - 亮蓝色
				:deep(.hljs-type),
				:deep(.hljs-class) {
					color: #79c0ff;
					font-weight: 600;
				}

				// 符号 - 亮橙色
				:deep(.hljs-symbol),
				:deep(.hljs-bullet) {
					color: #ffa657;
				}

				// 元信息 - 灰色
				:deep(.hljs-meta) {
					color: #8b949e;
				}

				// 操作符 - 白色
				:deep(.hljs-operator) {
					color: #e6edf3;
				}

				// 正则表达式 - 亮蓝色
				:deep(.hljs-regexp) {
					color: #79c0ff;
				}

				// 强调
				:deep(.hljs-emphasis) {
					font-style: italic;
				}

				// 加粗
				:deep(.hljs-strong) {
					font-weight: bold;
				}
			}
		}
	}
}

.viewer-header {
	height: 56px;
	padding: 0 16px;
	background: #fafafa;
	border-bottom: 1px solid #e8e8e8;
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-shrink: 0;

	.header-left {
		flex: 1;
		min-width: 0;

		.file-name {
			font-size: 14px;
			font-weight: 600;
			color: #262626;
			overflow: hidden;
			text-overflow: ellipsis;
			white-space: nowrap;
		}
	}

	.header-right {
		display: flex;
		align-items: center;
		gap: 8px;

		.search-container {
			display: flex;
			align-items: center;
			gap: 8px;
			margin-right: 8px;

			.search-input {
				width: 200px;
			}

			.search-info {
				display: flex;
				align-items: center;

				.search-position {
					font-size: 12px;
					color: #666;
					background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
					color: #fff;
					padding: 4px 8px;
					border-radius: 12px;
					font-weight: 500;
					box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
					min-width: 50px;
					text-align: center;
				}
			}

			.search-controls {
				display: flex;
				gap: 4px;
			}
		}

		:deep(.ant-btn) {
			border: none;
			box-shadow: none;
			display: flex;
			align-items: center;
			justify-content: center;

			&:hover {
				background: rgba(0, 0, 0, 0.06);
			}

			&.active {
				background: #1890ff;
				color: white;

				&:hover {
					background: #40a9ff;
				}
			}
		}
	}
}

.viewer-content {
	flex: 1;
	position: relative;
	overflow: auto;
	height: 100%;

	.code-container {
		position: relative;
		width: 100%;
		height: auto;

		.code-block {
			margin: 0;
			padding: 16px;
			background: #f8f9fa;
			border: none;
			border-radius: 0;
			font-family: 'Fira Code', 'Monaco', 'Cascadia Code', 'Roboto Mono', 'Consolas', 'Courier New', monospace;
			font-size: 13px;
			line-height: 1.6;
			white-space: pre;
			word-wrap: normal;
			overflow-wrap: normal;
			box-sizing: border-box;
			width: max-content;
			min-width: 100%;

			&.dark-theme {
				background: #1e1e1e;
				color: #d4d4d4;
			}

			code {
				display: block;
				padding: 0;
				background: none;
				border: none;
				font-family:
					'Fira Code', 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Consolas', 'Courier New', monospace;
				font-size: 14px;
				line-height: 1.45;
				color: #24292f;
				font-weight: 400;
				letter-spacing: 0.025em;
				white-space: pre;
				tab-size: 4;

				// 代码行样式
				:deep(.code-line) {
					display: flex;
					align-items: flex-start;
					min-height: 20px;
					white-space: pre;
					word-wrap: normal;
					overflow-wrap: normal;
					min-width: max-content;

					&:hover {
						background: rgba(175, 184, 193, 0.2);
					}
				}

				// 明亮主题下的代码高亮样式 - 参考豆包风格
				color: #24292f;
				background: #ffffff;

				// 关键字 - 深红色
				:deep(.hljs-keyword),
				:deep(.hljs-selector-tag),
				:deep(.hljs-literal),
				:deep(.hljs-section),
				:deep(.hljs-link) {
					color: #cf222e;
					font-weight: 600;
				}

				// 字符串 - 深蓝色
				:deep(.hljs-string) {
					color: #0a3069;
					font-weight: 500;
				}

				// 注释 - 灰色
				:deep(.hljs-comment),
				:deep(.hljs-quote) {
					color: #656d76;
					font-style: italic;
					font-weight: 400;
				}

				// 数字 - 蓝色
				:deep(.hljs-number) {
					color: #0550ae;
					font-weight: 500;
				}

				// 内置函数 - 蓝色
				:deep(.hljs-built_in),
				:deep(.hljs-builtin-name) {
					color: #0550ae;
					font-weight: 500;
				}

				// 函数名 - 紫色
				:deep(.hljs-title.function_),
				:deep(.hljs-function),
				:deep(.hljs-title) {
					color: #8250df;
					font-weight: 600;
				}

				// 属性和标签属性 - 紫色
				:deep(.hljs-attr),
				:deep(.hljs-attribute) {
					color: #8250df;
					font-weight: 500;
				}

				// HTML标签名 - 绿色
				:deep(.hljs-name),
				:deep(.hljs-tag) {
					color: #116329;
					font-weight: 600;
				}

				// 变量和模板变量 - 橙色
				:deep(.hljs-variable),
				:deep(.hljs-template-variable) {
					color: #953800;
					font-weight: 500;
					background: rgba(149, 56, 0, 0.1);
					padding: 2px 4px;
					border-radius: 3px;
				}

				// 类型 - 蓝色
				:deep(.hljs-type),
				:deep(.hljs-class) {
					color: #0550ae;
					font-weight: 600;
				}

				// 符号 - 橙色
				:deep(.hljs-symbol),
				:deep(.hljs-bullet) {
					color: #953800;
				}

				// 元信息 - 灰色
				:deep(.hljs-meta) {
					color: #656d76;
				}

				// 操作符 - 黑色
				:deep(.hljs-operator) {
					color: #24292f;
				}

				// 正则表达式 - 蓝色
				:deep(.hljs-regexp) {
					color: #0550ae;
				}

				// 强调
				:deep(.hljs-emphasis) {
					font-style: italic;
				}

				// 加粗
				:deep(.hljs-strong) {
					font-weight: bold;
				}

				:deep(.line-number) {
					display: inline-flex;
					width: 60px;
					align-items: center;
					justify-content: space-between;
					padding-right: 12px;
					color: #a0aec0;
					border-right: 1px solid #e2e8f0;
					margin-right: 12px;
					user-select: none;
					font-weight: 500;
					flex-shrink: 0;
					position: relative;
				}

				:deep(.line-num) {
					text-align: right;
					flex: 1;
				}

				:deep(.fold-control-wrapper) {
					display: inline-flex;
					width: 16px;
					justify-content: center;
					align-items: center;
				}

				:deep(.fold-control) {
					cursor: pointer;
					color: #6b7280;
					font-size: 12px;
					transition: color 0.2s ease;
					user-select: none;

					&:hover {
						color: #3b82f6;
					}
				}

				:deep(.line-content) {
					display: inline-block;
					white-space: pre;
					word-break: normal;
					overflow-wrap: normal;
					min-width: 0;
					width: auto;
				}

				// 搜索高亮样式 - 黄色背景
				:deep(.search-highlight) {
					background: #ffeb3b;
					color: #333;
					padding: 2px 4px;
					border-radius: 3px;
					box-shadow: 0 1px 3px rgba(255, 235, 59, 0.5);
					transition: all 0.2s ease;
					font-weight: 500;
					border: 1px solid #fbc02d;

					&.current {
						background: #ff9800;
						color: #fff;
						box-shadow: 0 2px 8px rgba(255, 152, 0, 0.6);
						transform: scale(1.05);
						border: 1px solid #f57c00;
						animation: pulse 1.5s infinite;
					}
				}

				// 脉冲动画
				@keyframes pulse {
					0%,
					100% {
						box-shadow: 0 2px 8px rgba(255, 152, 0, 0.6);
					}
					50% {
						box-shadow: 0 4px 12px rgba(255, 152, 0, 0.8);
					}
				}
			}

			// 暗黑主题下的行号样式
			&.dark-theme code :deep(.line-number) {
				color: #6e7681;
				border-right-color: #21262d;
			}

			// 暗黑主题下的折叠控制样式
			&.dark-theme :deep(.fold-control) {
				color: #8b949e;

				&:hover {
					color: #58a6ff;
				}
			}

			// 暗黑主题下的搜索高亮 - 黄色系配色
			&.dark-theme {
				:deep(.search-highlight) {
					background: #ffd60a !important;
					color: #000 !important;
					padding: 2px 4px;
					border-radius: 3px;
					box-shadow: 0 1px 3px rgba(255, 214, 10, 0.5);
					transition: all 0.2s ease;
					font-weight: 500;
					border: 1px solid #ffb700;

					&.current {
						background: #ff6b35 !important;
						color: #fff !important;
						box-shadow: 0 2px 8px rgba(255, 107, 53, 0.6);
						transform: scale(1.05);
						border: 1px solid #e55100;
						animation: pulse 1.5s infinite;
					}
				}

				.search-container .search-info .search-position {
					background: #ffc107;
					color: #000;
					box-shadow: 0 2px 8px rgba(255, 193, 7, 0.4);
				}

				code :deep(.search-highlight) {
					background: #ffc107;
					color: #000;
					box-shadow: 0 1px 3px rgba(255, 193, 7, 0.5);
					border: 1px solid #ffb300;

					&.current {
						background: #ff6f00;
						color: #fff;
						box-shadow: 0 2px 8px rgba(255, 111, 0, 0.6);
						transform: scale(1.05);
						border: 1px solid #e65100;
						animation: pulse-dark 1.5s infinite;
					}
				}

				.code-line:hover {
					background: rgba(255, 193, 7, 0.1) !important;
				}
			}

			// 暗黑主题脉冲动画
			@keyframes pulse-dark {
				0%,
				100% {
					box-shadow: 0 2px 8px rgba(255, 111, 0, 0.6);
				}
				50% {
					box-shadow: 0 4px 12px rgba(255, 111, 0, 0.8);
				}
			}
		}
	}
}

// 代码折叠样式
:deep(.fold-control) {
	cursor: pointer;
	margin-left: 4px;
	color: #666;
	font-size: 12px;
	user-select: none;
	transition: all 0.2s ease;
	display: inline-block;
	width: 12px;
	text-align: center;

	&:hover {
		color: #1890ff;
		transform: scale(1.2);
	}

	// HTML标签折叠控件特殊样式
	&[data-type='html'] {
		color: #e74c3c;

		&:hover {
			color: #c0392b;
		}
	}
}

:deep(.hidden-line) {
	display: none !important;
}

:deep(.folded-start) {
	position: relative;

	&::after {
		content: '...';
		color: #999;
		font-style: italic;
		margin-left: 8px;
		opacity: 0.7;
		cursor: pointer;
		padding: 2px 4px;
		border-radius: 3px;
		transition: all 0.2s ease;
	}

	&:hover::after {
		background-color: rgba(0, 0, 0, 0.1);
		opacity: 1;
	}

	.line-content {
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
}

:deep(.code-line) {
	transition: all 0.3s ease;
}

// 暗黑主题下的折叠控件样式
.dark-theme {
	:deep(.fold-control) {
		color: #8b949e;

		&:hover {
			color: #58a6ff;
		}

		&[data-type='html'] {
			color: #f85149;

			&:hover {
				color: #da3633;
			}
		}
	}

	:deep(.folded-start::after) {
		color: #6e7681;
	}
}

// 响应式设计
@media (max-width: 1200px) {
	.code-viewer-panel {
		width: 60%;
	}
}

@media (max-width: 768px) {
	.code-viewer-panel {
		width: 100%;
	}

	.viewer-header {
		.header-right {
			.search-container {
				width: 150px;
			}
		}
	}
}
</style>

<!-- 全局tooltip样式，不使用scoped -->
<style lang="scss">
// 简化的tooltip样式，确保显示
.ant-tooltip {
	.ant-tooltip-inner {
		background-color: rgba(0, 0, 0, 0.85) !important;
		color: #fff !important;
		font-size: 12px !important;
		border-radius: 6px !important;
		padding: 6px 8px !important;
		box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;
		z-index: 99999 !important; // 设置更高的z-index，超过CodeViewer的9999
	}

	.ant-tooltip-arrow {
		&::before {
			background-color: rgba(0, 0, 0, 0.85) !important;
		}
	}
}

// 暗黑主题下的tooltip样式
.dark-theme .ant-tooltip {
	.ant-tooltip-inner {
		background-color: rgba(255, 255, 255, 0.9) !important;
		color: #000 !important;
		font-weight: 500 !important;
	}

	.ant-tooltip-arrow {
		&::before {
			background-color: rgba(255, 255, 255, 0.9) !important;
		}
	}
}

// 确保tooltip容器也有足够高的z-index
.ant-tooltip {
	z-index: 99999 !important;
}
</style>
